<?php
namespace app\api\controller\v1;

use app\api\model\v1\Task as TaskModel;
use think\Db;

/**
 * 任务
 * 访问：/api/v1.task/方法名；
 */
class Task extends Base
{
    private $partakeName = '报名';

    // 任务广场的任务列表
    public function listx() {
        /*
        SELECT y.*,tu.user_id,Case 
                WHEN tu.user_id IS NULL THEN
                        '未读'
                ELSE
                        '已读'
        END AS ss
            FROM (
        SELECT t.id, t.name, t.illustration, t.content, t.begin_at, t.end_at, t.need_time, t.reward, t.created_at, t.partake_limit, COUNT(t.id) AS partake_count
        FROM yx_task AS t
        LEFT JOIN yx_task_user AS tu ON t.id=tu.task_id
        WHERE t.id NOT IN (SELECT task_id FROM yx_task_user WHERE user_id=30 AND (receive_at IS NOT NULL OR deleted_at IS NOT NULL)) 
            AND t.status <> '1' 
            AND t.end_at > CURRENT_TIMESTAMP()
            AND t.deleted_at IS NULL
        GROUP BY t.id
        ) AS y
        LEFT JOIN (SELECT task_id,user_id FROM yx_task_user WHERE user_id=30 AND receive_at IS NULL AND deleted_at IS NULL) AS tu ON tu.task_id=y.id
        WHERE partake_count<partake_limit
        */
        try {
            // page App中上拉加载更多时使用，传递App中已经加载了几次；默认就有了一次；
            // 每次返回 20 条；
            $data = $this->jmParameter(['account_id', 'page', 'action']);
            $validateResult = $this->validate($data,
                $this->jmSetRule([
                    'page' => ['regex' => \jm\helper\Check::$regexpArray['id']],
                    'action|动作类型' => 'in:unreceive,receive,complete'
                    // 'action|' => ['regex' => '/^(unreceive|receive|complete)$/']
                ]),
                $this->jmSetMessage([
                    'page.regex' => 'page无效',
                    'action.in' => '动作类型无效'
                ])
            );
            if (true !== $validateResult) {
                return $this->jmReturnJson(4001, $validateResult);
            }

            $this->jmUnsetEmptyAndTrans2int($data, ['account_id', 'page']);

            if (empty($data['page'])) {
                $data['page'] = 1;
            }

            $field = ['t.id', 't.name', 't.illustration', 't.content', 't.begin_at', 't.end_at', 't.need_time', 't.reward', 't.created_at'];

            if (empty($data['action']) || $data['action'] === 'unreceive') {
                // 查询所有进行中并且当前用户尚未参与的任务
                // status 为 0的，并且当前时间在结束时间之前；
                /*
                $taskModels = TaskModel::alias('t')
                    ->field(array_merge($field, ['COUNT(tu.id)' => 'count', 'IFNULL()']))
                    ->join('task_user tu', 'tu.task_id=t.id', 'left')
                    ->where('t.status', '0')// 所有未结束的任务（含未开始）；
                    ->where('t.status', '0')// 所有未结束的任务（含未开始）；
                    ->where('t.id', 'NOT IN', function($query) use ($data) {
                        // 此种形式不会自动使用软删除的 traits，所以需要手动写出 deleted_at IS NULL；
                        // $query->name('task_user')->where([
                        //     'user_id' => $data['account_id'],
                        //     'deleted_at' => ['exp', 'IS NULL']// 这样不行
                        // ])->field('task_id');
                        $query->name('task_user')->where('user_id', $data['account_id'])->where('receive_at', 'NOT NULL')->where('deleted_at', 'NULL')->field('task_id');
                    })
                    ->group('t.id')
                    ->paginate($this->pageSize);
                */
                
                /*
                $subQuery = Db::name('task t')
                    ->field('t.id, t.name, t.illustration, t.content, t.begin_at, t.end_at, t.need_time, t.reward, t.created_at, t.partake_limit, COUNT(t.id) AS partake_count')
                    ->join('yx_task_user tu', 'tu.task_id=t.id', 'left')
                    ->where('t.id', 'NOT IN', function($query) use ($data) {
                        $query->name('task_user')
                            ->where('user_id', $data['account_id'])
                            // ->where('receive_at', 'NOT NULL')
                            // ->whereOr('deleted_at', 'NOT NULL')
                            ->where(function ($query) {
                                $query->whereNotNull('receive_at')->whereOr('deleted_at', 'NOT NULL');
                            })
                            ->field('task_id');
                    })
                    ->where('t.status', '0')
                    ->where('t.end_at', 'exp', ' > CURRENT_TIMESTAMP()')
                    ->where('t.deleted_at', 'NULL')
                    // ->whereNotNull('tu.receive_at')
                    ->group('t.id')
                    ->buildSql();
                
                $taskModels = Db::name($subQuery.' y')
                    ->field('y.*, Case WHEN tt.user_id IS NULL THEN 0 ELSE 1 END AS read_status')
                    ->where('partake_count', 'exp', ' < partake_limit')
                    ->join('(SELECT task_id,user_id FROM yx_task_user WHERE user_id='. $data['account_id'] .' AND receive_at IS NULL AND deleted_at IS NULL) tt', 'tt.task_id=y.id', 'left')
                    ->order('y.id','DESC')
                    ->paginate($this->pageSize);
                */
               
                // 去除以下任务；
                // 删除的任务
                // 过期的任务
                // 未过期、但提前关闭的任务（当前已经处于关闭状态的）
                // 用户已经报名的任务（用户取消的，也算是报名过的，也不显示）
                // 用户被禁止参加的任务
                
                // 参加者已经达到上限的任务（这个还是显示吧，不让参加而已~）
               
                $subQuery = Db::name('task t')
                    // ->field('t.id, t.name, t.illustration, t.content, t.begin_at, t.end_at, t.need_time, t.reward, t.created_at, t.partake_limit, partake_count')
                    ->field('t.id, t.name, t.illustration, t.content, t.begin_at, t.end_at, concat(substring(t.begin_at, 1, 10), " 至 ", substring(t.end_at, 1, 10)) AS need_time, t.reward, t.created_at, t.partake_limit, partake_count')
                    ->where('t.id', 'NOT IN', function($query) use ($data) {
                        $query->name('task_user')
                            ->where('user_id', $data['account_id'])
                            ->where(function ($query) {
                                $query->whereNotNull('receive_at')->whereOr('deleted_at', 'NOT NULL');
                            })
                            ->field('task_id');
                    })
                    ->where('t.status', '0')
                    ->where('t.end_at', 'exp', ' > CURRENT_TIMESTAMP()')
                    ->where('t.deleted_at', 'NULL')
                    ->buildSql();
                
				// 此处不能用 name 方法，因为name会自动加前缀，导致执行的sql变成了很怪异的：
				// ( _s_e_l_e_c_t `t`.`id`,`t`.`name`,`t`.`illustration`,`t`.`content`,`t`.`begin_a
				// $taskModels = Db::name($subQuery)->alias('y'); 还是一样报错的；
				// 总之使用 table方法就是可以的。
                $taskModels = Db::table($subQuery .' y')
                    // partake_limit 为 0 表示没有上限；
                    ->field('y.*, CASE WHEN tt.user_id IS NULL THEN 0 ELSE 1 END AS read_status, CASE WHEN y.partake_limit=0 THEN true WHEN y.partake_limit>y.partake_count THEN true ELSE false END AS can_partake')
					// 此时的join 不会自动加表前缀，而是按照我们预想的在执行；
                    ->join('(SELECT task_id,user_id FROM '. $this->dbTablePrefix .'task_user WHERE user_id='. $data['account_id'] .' AND receive_at IS NULL AND deleted_at IS NULL) tt', 'tt.task_id=y.id', 'left')
                    ->order('y.id','DESC')
                    ->paginate(\think\Config::get('glhf_task.page_size'));
            } else if ($data['action'] === 'receive') {
                /*
                $taskModels = TaskModel::alias('t')
                    ->field(array_merge($field, ['tu.content', 'tu.receive_at', 'tu.submit_at', 'tu.status', 'tu.conclusion', 'tu.conclude_at']))
                    ->join('task_user tu', 'tu.task_id=t.id', 'left')
                    ->where('t.id', 'IN', function($query) use ($data) {
                        // 所有已参加的任务；
                        $query->name('task_user')
                            ->where('user_id', $data['account_id'])
                            ->where('receive_at', 'NOT NULL')
                            ->where('deleted_at', 'NULL')// 用户已参加，但又被禁止参加的任务，不显示；
                            ->field('task_id');
                    })
                    ->group('t.id')
                    ->paginate($this->pageSize);
                */
               
                $subQuery = Db::name('task t')
                    // ->field('t.id, t.name, t.illustration, t.content AS task_content, t.begin_at, t.end_at, t.need_time, t.reward, t.status AS task_status, t.created_at, t.partake_limit, partake_count')
                    ->field('t.id, t.name, t.illustration, t.content AS task_content, t.begin_at, t.end_at, concat(substring(t.begin_at, 1, 10), " 至 ", substring(t.end_at, 1, 10)) AS need_time, t.reward, t.status AS task_status, t.created_at, t.partake_limit, partake_count')
                    ->where('t.id', 'IN', function($query) use ($data) {
                        $query->name('task_user')
                            ->where('user_id', $data['account_id'])
                            ->whereNotNull('receive_at')
                            ->whereNull('deleted_at')
                            ->field('task_id');
                    })
                    ->whereNull('t.deleted_at')
                    ->buildSql();
                /*
                $taskModels = Db::name($subQuery.' y')
                    ->field('y.*, tt.*, CASE WHEN y.task_status="1" THEN 0 WHEN y.end_at<CURRENT_TIMESTAMP() THEN 0 WHEN tt.receive_at IS NOT NULL THEN 0 ELSE 1 END AS can_partake')
                    ->join('(SELECT task_id,content,receive_at,submit_at,status,CASE status WHEN "0" THEN "待提交" WHEN "1" THEN "审核中" WHEN "2" THEN "已取消" WHEN "3" THEN "未通过审核" WHEN "4" THEN "通过审核" END AS status_text, conclusion,conclude_at FROM yx_task_user WHERE user_id='. $data['account_id'] .' AND receive_at IS NOT NULL AND deleted_at IS NULL) tt', 'tt.task_id=y.id', 'left')
                    ->order('y.id','DESC')
                    ->paginate(\think\Config::get('glhf_task.page_size'));
                */
                /*
                $taskModels = Db::name($subQuery.' y')
                    ->field('y.*, tt.*, CASE WHEN y.task_status="1" THEN 0 WHEN y.end_at<CURRENT_TIMESTAMP() THEN 0 WHEN tt.receive_at IS NOT NULL THEN 0 ELSE 1 END AS can_partake, CASE WHEN y.end_at<CURRENT_TIMESTAMP() THEN 2 WHEN y.task_status="1" THEN 1 END AS task_progress_status')
                    ->join('(SELECT task_id,content,receive_at,submit_at,status,CASE status WHEN "0" THEN "待提交" WHEN "1" THEN "审核中" WHEN "2" THEN "已取消" WHEN "3" THEN "未通过审核" WHEN "4" THEN "通过审核" END AS status_text, conclusion,conclude_at FROM yx_task_user WHERE user_id='. $data['account_id'] .' AND receive_at IS NOT NULL AND deleted_at IS NULL) tt', 'tt.task_id=y.id', 'left')
                    ->order('y.id','DESC')
                    ->paginate(\think\Config::get('glhf_task.page_size'));
                */
               
               // CASE WHEN y.task_status="1" THEN 0 WHEN y.end_at<CURRENT_TIMESTAMP() THEN 0 WHEN tt.receive_at IS NOT NULL THEN 0 ELSE 1 END AS can_partake, 
                
                $subQuery2 = Db::table($subQuery .' y')
                    ->field('y.*, tt.user_content,tt.receive_at,tt.submit_at,tt.user_task_status,tt.user_task_status_text,tt.conclusion,tt.conclude_at, CASE WHEN y.end_at<CURRENT_TIMESTAMP() THEN 2 WHEN y.task_status="1" THEN 1 ELSE 0 END AS task_progress_status')
                    ->join('(SELECT task_id,content AS user_content,receive_at,submit_at,status AS user_task_status,CASE status WHEN "1" THEN "已报名" WHEN "2" THEN "审核中" WHEN "3" THEN "已取消" WHEN "4" THEN "未通过审核" WHEN "5" THEN "通过审核" END AS user_task_status_text, conclusion,conclude_at FROM '. $this->dbTablePrefix .'task_user WHERE user_id='. $data['account_id'] .' AND receive_at IS NOT NULL AND deleted_at IS NULL) tt', 'tt.task_id=y.id', 'left')
                    ->buildSql();

                $taskModels = Db::table($subQuery2 .' y2')
                    ->field('y2.*, CASE y2.task_progress_status WHEN 1 THEN "已关闭" WHEN 2 THEN "已过期" ELSE "进行中" END AS task_progress_status_text')
                    ->order('y2.id','DESC')
                    ->paginate(\think\Config::get('glhf_task.page_size'));
            } else if ($data['action'] === 'complete') {
                // 当前用户已完成的任务
                // 只有 status 为 5 审核通过的；
                
                $subQuery = Db::name('task')->alias('t')
                    // ->field('t.id, t.name, t.illustration, t.content AS task_content, t.begin_at, t.end_at, t.need_time, t.reward, t.created_at, t.partake_limit, partake_count')
                    ->field('t.id, t.name, t.illustration, t.content AS task_content, t.begin_at, t.end_at, concat(substring(t.begin_at, 1, 10), " 至 ", substring(t.end_at, 1, 10)) AS need_time, t.reward, t.created_at, t.partake_limit, partake_count')
                    ->where('t.id', 'IN', function($query) use ($data) {
                        $query->name('task_user')
                            ->where('user_id', $data['account_id'])
                            ->where('status', '5')
                            // ->whereNotNull('receive_at')
                            ->whereNull('deleted_at')
                            ->field('task_id');
                    })
                    ->whereNull('t.deleted_at')
                    ->buildSql();
                
                $taskModels = Db::table($subQuery.' y')
                    ->field('y.*, tt.user_content,tt.receive_at,tt.submit_at,tt.user_task_status,tt.user_task_status_text,tt.conclusion,tt.conclude_at')
                    ->join('(SELECT task_id,content AS user_content,receive_at,submit_at,status AS user_task_status,"通过审核" AS user_task_status_text, conclusion,conclude_at FROM '. $this->dbTablePrefix .'task_user WHERE user_id='. $data['account_id'] .' AND receive_at IS NOT NULL AND deleted_at IS NULL) tt', 'tt.task_id=y.id', 'left')
                    ->order('y.id','DESC')
                    ->paginate(\think\Config::get('glhf_task.page_size'));
            }
            
            return $this->jmReturnJson(200, '获取任务列表数据成功', [
                'list' => $taskModels->items(),
                // 'taskModels' => $taskModels,
                'hasMore' => $taskModels->lastPage() > $data['page']
            ]);
        } catch (\Exception $e) {
            return $this->jmException($e);
        }
    }

    // 标记已读；此时在 task_user 表中新增一条记录即可。
    public function readTag() {
        try {
            $data = $this->jmParameter(['account_id', 'task_id']);
            $validateResult = $this->validate($data,
                $this->jmSetRule(['task_id|任务ID' => ['regex' => \jm\helper\Check::$regexpArray['id']]]),
                $this->jmSetMessage(['task_id.regex' => '任务ID无效',])
            );
            if (true !== $validateResult) {
                return $this->jmReturnJson(4001, $validateResult);
            }

            $this->jmTrans2int($data, ['account_id', 'task_id']);

            $data = [
                'task_id' => $data['task_id'],
                'user_id' => $data['account_id']
            ];

            // 先检查是否已有此记录；
            $row = Db::name('task_user')->field(['task_id', 'user_id'])->where($data)->whereNull('deleted_at')->find();
            if (!is_null($row)) {
                return $this->jmReturnJson(200, '标记已读状态成功');
            }

            $insertRows = Db::name('task_user')->insert($data);
            if ($insertRows) {
                return $this->jmReturnJson(200, '标记已读状态成功');
            }
            return $this->jmReturnJson(4003, '标记已读状态失败');
        } catch (\Exception $e) {
            return $this->jmException($e);
        }
    }

    // 报名参与任务
    // for update 控制同一时间只有一个人操作；
    public function partake() {
        Db::startTrans();
        try {
            $data = $this->jmParameter(['account_id', 'task_id']);
            $validateResult = $this->validate($data,
                $this->jmSetRule(['task_id|任务ID' => ['require', 'regex' => \jm\helper\Check::$regexpArray['id']]]),
                $this->jmSetMessage(['task_id.regex' => '任务ID无效'])
            );
            if (true !== $validateResult) {
                return $this->jmReturnJson(4001, $validateResult);
            }

            $this->jmTrans2int($data, ['account_id', 'task_id']);

            // 理论上说，应该验证是否已经参加过指定任务；但由于任务列表中显示的是未参加过的，所以就不进行验证了，直接写入中间表；

            // 但是需要检查该任务的参加者数量是否已达上限；
            
            // 这是可以的，但是无法兼顾 partake_limit 为 0 的情况；
            // $taskModel = TaskModel::field(['partake_limit', 'partake_count'])->having('partake_limit > partake_count')->find($data['task_id']);
            
            $taskModel = TaskModel::field(['partake_limit', 'partake_count', 'begin_at'])->lock(true)->find($data['task_id']);
            if (is_null($taskModel) || ($taskModel['partake_limit'] !== 0 && $taskModel['partake_limit'] <= $taskModel['partake_count'])) {
                Db::rollback();
                return $this->jmReturnJson(4002, '参加者已达上限，请'. $this->partakeName .'其他任务');
            }
            // 执行到这里，$taskModel 一定不是 null 了；
            // 不能参加未开始的任务；
            if ($taskModel['begin_at'] > date('Y-m-d H:i:s')) {
                return $this->jmReturnJson(4002, '该任务尚未开始');
            }

            // 更新            
            $affectedRows = Db::name('task_user')->where([
                'task_id' => $data['task_id'],
                'user_id' => $data['account_id']
            ])->whereNull('deleted_at')->update([
                'receive_at' => \jm\helper\DateTime::YMDHISWithLine(),
                'status' => '1',
            ]);

            if (!$affectedRows) {
                Db::rollback();
                return $this->jmReturnJson(4003, $this->partakeName .'失败，请稍后再试');
            }

            // task 表 partake_count++；
            $affectedRows = TaskModel::where('id', $data['task_id'])->setInc('partake_count');
            if (!$affectedRows) {
                Db::rollback();
                return $this->jmReturnJson(4004, $this->partakeName .'失败，请稍后再试');
            }

            // \Dev::test("1 -- " . $taskModel->partake_limit);// 即使在commit 之前，也是能取到值的；

            Db::commit();

            // \Dev::test("2 -- " . $taskModel->partake_limit);// 在commit 之后，自然也是能取到值的；

            return $this->jmReturnJson(200, $this->partakeName .'成功');
        } catch (\Exception $e) {
            Db::rollback();
            return $this->jmException($e);
        }
    }

    // 提交任务时，是否被（提前）关闭、是否过期、是否被用户已经取消；
    // 上传的文件，既可以是用户的线上已有文件，也可以使用用户手机内的文件；
    public function submitTask() {
        try {
            $data = $this->jmParameter(['account_id', 'task_id', 'file_id']);
            $validateResult = $this->validate($data,
                $this->jmSetRule([
                    'task_id|任务ID' => ['require', 'regex' => \jm\helper\Check::$regexpArray['id']],
                    'file_id|文件' => 'require'
                ]),
                $this->jmSetMessage(['file_id.require' => '请选择文件'])
            );
            if (true !== $validateResult) {
                return $this->jmReturnJson(4001, $validateResult);
            }

            $this->jmTrans2int($data, ['account_id', 'task_id']);

            $taskModel = TaskModel::alias('t')
                ->field(['t.id', 't.status'=>'task_status', 't.end_at', 'tu.status'=>'user_task_status'])
                ->join('task_user tu', 'tu.task_id=t.id')
                ->where('t.id', $data['task_id'])
                ->where('tu.user_id', $data['account_id'])
                ->whereNull('tu.deleted_at')
                ->find();
            if (is_null($taskModel)) {
                return $this->jmReturnJson(4002, '指定任务不存在');
            }

            if ($taskModel['end_at'] < \jm\helper\DateTime::YMDHISWithLine()) {
                return $this->jmReturnJson(4003, '任务已过期');
            }

            if ($taskModel['task_status'] === '1') {
                return $this->jmReturnJson(4004, '任务已关闭');
            }

            if ($taskModel['user_task_status'] === '2') {
                return $this->jmReturnJson(4005, '任务已被取消');
            }

            $affectedRows = Db::name('task_user')->where([
                'user_id' => $data['account_id'],
                'task_id' => $data['task_id']
            ])->update([
                'answer' => $data['file_id'],
                'submit_at' => \jm\helper\DateTime::YMDHISWithLine(),
                'status' => '2'
            ]);
            if (!$affectedRows) {
                return $this->jmReturnJson(4006, '提交失败');
            }

            return $this->jmReturnJson(200, '提交成功，等待审核');
        } catch (\Exception $e) {
            return $this->jmException($e);
        }
    }

    // 查询积分接口
    public function getRewardData() {
        try {
            $accountId = $this->jmParameter('account_id', 0);
            $validateResult = $this->jmValidateAccountId($accountId);
            if (true !== $validateResult) {
                return json($validateResult);
            }

            $res = $this->jmReward($accountId);
            if ($res['errcode'] !== 200) {
                return $this->jmReturnJson(4002, $res['message']);
            }

            return $this->jmReturnJson(200, $res['message'], ['reward' => $res['data']['reward']]);
        } catch (\Exception $e) {
            return $this->jmException($e);
        }
    }

    // 提取积分接口（新增提取记录）
    public function pickupReward() {
        try {
            $data = $this->jmParameter(['account_id', 'reward']);
            $validateResult = $this->validate($data,
                $this->jmSetRule(['reward|提取积分值' => 'require|number']),
                $this->jmSetMessage()
            );
            if (true !== $validateResult) {
                return $this->jmReturnJson(4001, $validateResult);
            }

            $this->jmTrans2int($data, ['account_id', 'reward']);

            if ($data['reward'] < 0) {
                return $this->jmReturnJson(4002, '提取积分值必须大于0');
            }

            $rewardData = $this->jmReward($data['account_id']);
            if ($rewardData['errcode'] !== 200) {
                return $this->jmReturnJson(4003, $res['message']);
            }

            if ($rewardData['data']['reward'] < $data['reward']) {
                return $this->jmReturnJson(4004, '提取积分值超过了可用值');
            }

            // 新增提取记录

            // 查询所有进行中并且当前用户尚未参与的任务
            $insertRows = Db::name('reward_log')->insert(['user_id'=>$data['account_id'], 'reward'=>$data['reward']]);
            if ($insertRows) {
                return $this->jmReturnJson(200, '提取成功');
            }
            
            return $this->jmReturnJson(4005, '提取失败');
        } catch (\Exception $e) {
            return $this->jmException($e);
        }
    }

    // 取消任务
    public function cancelTask() {
        try {
            $data = $this->jmParameter(['account_id', 'task_id']);
            $validateResult = $this->validate($data,
                $this->jmSetRule(['task_id|任务ID' => ['require', 'regex' => \jm\helper\Check::$regexpArray['id']]]),
                $this->jmSetMessage(['task_id.regex' => '任务ID无效'])
            );
            if (true !== $validateResult) {
                return $this->jmReturnJson(4001, $validateResult);
            }

            $this->jmTrans2int($data, ['account_id', 'task_id']);

            // 查询所有进行中并且当前用户尚未参与的任务
            $affectedRows = Db::name('task_user')->where(['user_id'=>$data['account_id'], 'task_id'=>$data['task_id']])
                ->update([
                    'status' => '3',
                    'cancel_at' => \jm\helper\DateTime::YMDHISWithLine()
                ]);
            if ($affectedRows) {
                return $this->jmReturnJson(200, '取消任务成功');
            }
            
            return $this->jmReturnJson(4002, '取消任务失败');
        } catch (\Exception $e) {
            return $this->jmException($e);
        }
    }
}